﻿//========================================================= 
/**@file SocketDevice.h
 * @brief Linux下Socket通信
 * 
 * @date 2018-12-13   11:18:04
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_SOCKETDEVICE_H_
#define _LIBZHOUYB_SOCKETDEVICE_H_
//--------------------------------------------------------- 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "FileDevice.h"
//--------------------------------------------------------- 
namespace zhou_yb {
namespace base_device {
namespace env_linux {
//--------------------------------------------------------- 
/// SOCKET错误码
#ifndef SOCKET_ERROR
#   define SOCKET_ERROR (-1)
#endif
//--------------------------------------------------------- 
/**
 * @brief Linux下Socket通信设备
 * @date 2018-12-13 11:35
 */
class SocketHandlerFactory : public IFactory<FileHandler>, public LoggerBehavior
{
protected:
    //----------------------------------------------------- 
    /// 解析Socket参数
    bool _parse_socket(const char* sArg, int& af, int& type, int& protocol)
    {
        if(_is_empty_or_null(sArg))
            return false;
        ArgParser cfg;
        StringArguments arg;
        if(cfg.Parse(sArg, arg) < 1)
            return false;

        ArgConvert::FromConfig<int>(arg, SocketAfKey, af);
        ArgConvert::FromConfig<int>(arg, SocketTypeKey, type);
        ArgConvert::FromConfig<int>(arg, SocketProtocolKey, protocol);
        return true;
    }
    //----------------------------------------------------- 
public:
    //----------------------------------------------------- 
    /// SocketHandlerFactory生成参数配置项时的主键 SocketAfKey
    static const char SocketAfKey[16];
    /// SocketHandlerFactory生成参数配置项时的主键 SocketTypeKey
    static const char SocketTypeKey[16];
    /// SocketHandlerFactory生成参数配置项时的主键 SocketProtocolKey
    static const char SocketProtocolKey[16];
    //----------------------------------------------------- 
    /// 生成参数 
    static string ToArg(int af, int type, int protocol)
    {
        string sArg;
        sArg += ArgConvert::ToConfig<int>(SocketAfKey, af);
        sArg += ArgConvert::ToConfig<int>(SocketTypeKey, type);
        sArg += ArgConvert::ToConfig<int>(SocketProtocolKey, protocol);

        return sArg;
    }
    //----------------------------------------------------- 
    /// 创建句柄 
    virtual bool Create(FileHandler& obj, const char* sArg = NULL)
    {
        int af = 0;
        int type = 0;
        int protocol = 0;
        LOGGER(_log.WriteLine("ParseArg..."));
        if(!_parse_socket(sArg, af, type, protocol))
            return false;

        return Create(obj, af, type, protocol);
    }
    /// 创建socket
    bool Create(FileHandler& obj, int af, int type, int protocol)
    {
        LOGGER(_log << "Af:<" << af << ">,Type:<" << type << ">,Protocol:<" << protocol << ">\n");
        obj.Handle = socket(af, type, protocol);
        return IsValid(obj);
    }
    /// 返回句柄是否有效 
    virtual bool IsValid(const FileHandler& obj)
    {
        return obj.Handle != INVALID_FD_VALUE;
    }
    /// 释放句柄 
    virtual void Dispose(FileHandler& obj)
    {
        close(obj.Handle);
        obj.Handle = INVALID_FD_VALUE;
    }
    //----------------------------------------------------- 
};
//--------------------------------------------------------- 
/**
 * @brief Linux下异步Socket句柄工厂
 * @date 2019-03-20 17:47
 */
class AsyncSocketHandlerFactory : public SocketHandlerFactory
{
public:
    static bool SetNonblock(FileHandler& obj)
    {
        int flags = fcntl(obj.Handle, F_GETFL, 0);
        fcntl(obj.Handle, F_SETFL, flags | O_NONBLOCK);
        return true;
    }

    /// 创建句柄 
    virtual bool Create(FileHandler& obj, const char* sArg = NULL)
    {
        if(!SocketHandlerFactory::Create(obj, sArg))
            return false;
        if(!SetNonblock(obj))
        {
            SocketHandlerFactory::Dispose(obj);
            return false;
        }
        return true;
    }
};
//--------------------------------------------------------- 
/// Socket读写设备(同步)
typedef HandlerDevice<HandlerBaseDevice<FileHandler, SocketHandlerFactory>, FileHandlerReader, FileHandlerWriter> SocketHandlerDevice;
//--------------------------------------------------------- 
/// 同步Socket设备
class SocketDevice : public SocketHandlerDevice
{
public:
    SocketDevice() : SocketHandlerDevice() {}

    /// 连接 
    virtual bool Connect(const sockaddr* name, int namelen)
    {
        LOG_FUNC_NAME();
        ASSERT_DeviceValid(IsOpen());

        if(connect(_hDev.Handle, name, namelen) == SOCKET_ERROR)
        {
            _logErr(DeviceError::DevConnectErr, "connect失败");
            return _logRetValue(false);
        }

        return _logRetValue(true);
    }
    /// 绑定 
    virtual bool Bind(const sockaddr* name, int namelen)
    {
        LOG_FUNC_NAME();
        ASSERT_DeviceValid(IsOpen());

        if(bind(_hDev.Handle, name, namelen) == SOCKET_ERROR)
        {
            _logErr(DeviceError::DevConnectErr, "bind失败");
            return _logRetValue(false);
        }

        return _logRetValue(true);
    }
    /// 监听 
    virtual bool Listen(int backlog)
    {
        LOG_FUNC_NAME();
        ASSERT_DeviceValid(IsOpen());

        if(listen(_hDev.Handle, backlog) == SOCKET_ERROR)
        {
            _logErr(DeviceError::DevConnectErr, "listen失败");
            return _logRetValue(false);
        }

        return _logRetValue(true);
    }
};
//--------------------------------------------------------- 
/// Socket读写设备(异步)
typedef HandlerDevice<HandlerBaseDevice<FileHandler, AsyncSocketHandlerFactory>, AsyncFileHandlerReader, AsyncFileHandlerWriter> AsyncSocketHandlerDevice;
//---------------------------------------------------------
/// 异步Socket设备
class AsyncSocketDevice : public AsyncSocketHandlerDevice
{
protected:
    bool _wait_connect()
    {
        Timer timer;
        fd_selecter fds;
        fds.set_timeout(_waitInterval);

        while(timer.Elapsed() < _waitTimeout)
        {
            fds.reset(_hDev.Handle);
            if(fds.can_write())
                return true;
            if(InterruptBehavior::Implement(*this))
            {
                _logErr(DeviceError::OperatorInterruptErr, "等待事件被中断");
                return false;
            }
            Timer::Wait(_waitInterval);
        }
        _logErr(DeviceError::WaitTimeOutErr, "等待事件超时");
        return false;
    }
public:
    AsyncSocketDevice() : AsyncSocketHandlerDevice() {}

    /// 连接
    virtual bool Connect(const sockaddr* name, int namelen)
    {
        LOG_FUNC_NAME();
        ASSERT_DeviceValid(IsOpen());

        if(connect(_hDev.Handle, name, namelen) == 0)
            return _logRetValue(true);
        if(errno != EINPROGRESS)
        {
            _logErr(DeviceError::DevConnectErr, "connect失败");
            return _logRetValue(false);
        }
        if(!_wait_connect())
        {
            return _logRetValue(false);
        }
        socklen_t len = sizeof(errno);
        getsockopt(_hDev.Handle, SOL_SOCKET, SO_ERROR, (void*)&errno, &len);
        if(errno)
        {
            _logErr(DeviceError::DevStateErr, strerror(errno));
            return _logRetValue(false);
        }
        return _logRetValue(true);
    }
    /// 绑定
    virtual bool Bind(const sockaddr* name, int namelen)
    {
        LOG_FUNC_NAME();
        ASSERT_DeviceValid(IsOpen());

        if(bind(_hDev.Handle, name, namelen) == SOCKET_ERROR)
        {
            _logErr(DeviceError::DevConnectErr, "bind失败");
            return _logRetValue(false);
        }

        return _logRetValue(true);
    }
    /// 监听
    virtual bool Listen(int backlog)
    {
        LOG_FUNC_NAME();
        ASSERT_DeviceValid(IsOpen());

        if(listen(_hDev.Handle, backlog) == SOCKET_ERROR)
        {
            _logErr(DeviceError::DevConnectErr, "listen失败");
            return _logRetValue(false);
        }

        return _logRetValue(true);
    }
};
//--------------------------------------------------------- 
} // namespace env_linux
} // namespace base_device 
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_SOCKETDEVICE_H_
//========================================================= 